package top.lingkang.finalgateway.admin.terminal;

import cn.hutool.core.io.IoUtil;
import com.pty4j.PtyProcess;
import com.pty4j.PtyProcessBuilder;
import com.pty4j.WinSize;
import lombok.extern.slf4j.Slf4j;
import org.noear.socketd.transport.core.Session;
import org.noear.socketd.transport.core.entity.EntityDefault;
import top.lingkang.finalgateway.utils.CommonUtils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author lingkang
 * Created by 2024/5/19
 */
@Slf4j
public class TerminalHandler {
    private String id;
    private boolean isReady;
    private String[] termCommand;
    private PtyProcess process;
    private Integer columns = 50;
    private Integer rows = 10;
    private BufferedReader inputReader;
    private BufferedReader errorReader;
    private BufferedWriter outputWriter;

    public static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(0, Integer.MAX_VALUE,
            60L, TimeUnit.SECONDS,
            new SynchronousQueue<Runnable>());
    private final Session session;


    public TerminalHandler(Session session, String columns, String rows) {
        if (columns != null)
            this.columns = Integer.parseInt(columns);
        if (rows != null)
            this.rows = Integer.parseInt(rows);
        this.session = session;
        try {
            initializeProcess();
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        id = session.sessionId();
        log.info("终端初始化完成： sessionId = {}", id);

        try {
            EntityDefault entityDefault = new EntityDefault();
            entityDefault.dataSet("".getBytes(StandardCharsets.UTF_8));
            entityDefault.metaPut("type", "initComplete");
            session.send("/", entityDefault);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void initializeProcess() throws Throwable {
        if (isReady) {
            return;
        }
        String userHome = System.getProperty("user.home");
        if (CommonUtils.isWindow()) {
            this.termCommand = "powershell.exe".split("\\s+");
        } else if (CommonUtils.isUbuntu()) {
            termCommand = new String[]{"sh", "-i"};
        } else {
            this.termCommand = "/bin/sh -i".split("\\s+");
        }
        // String[] cmd = {"ssh", "user@hostname"};

        Map<String, String> envs = new HashMap<>(System.getenv());
        envs.put("TERM", "xterm");

        PtyProcessBuilder ptyProcessBuilder = new PtyProcessBuilder()
                .setCommand(termCommand)
                .setEnvironment(envs)
                .setDirectory(userHome);
        this.process = ptyProcessBuilder.start();
        // this.process = PtyProcess.exec(termCommand, envs, userHome);
        process.setWinSize(new WinSize(columns, rows));
        this.inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        this.errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
        this.outputWriter = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));

        if (CommonUtils.isLinux()) {
            // 进入当前用户
            if (CommonUtils.isCentOS()) {
                command("su ${whoami}\r\n");
            } else if (CommonUtils.isUbuntu()) {
                command("sleep 1 && echo \"请输入命令切换用户以获得更多权限: su \\${whoami}\"\r");
            }
        }

        threadPool.execute(() -> {
            printReader(inputReader);
        });
        threadPool.execute(() -> {
            printReader(errorReader);
        });

        this.isReady = true;
    }

    private void printReader(BufferedReader in) {
        try {
            int inRead;
            char[] data = new char[1024];
            while ((inRead = in.read(data, 0, data.length)) != -1) {
                print(String.valueOf(data, 0, inRead));
            }
            log.debug("线程关闭: {}", Thread.currentThread().getName());
        } catch (Exception e) {
            log.warn("io exception", e);
        }
    }

    public void print(String text) throws IOException {
        EntityDefault entityDefault = new EntityDefault();
        entityDefault.dataSet(text.getBytes(StandardCharsets.UTF_8));
        entityDefault.metaPut("type", "print");
        session.send("/", entityDefault);
    }

    public synchronized void command(String command) {
        if (Objects.isNull(command)) {
            return;
        }
        try {
            outputWriter.write(command);
            outputWriter.flush();
        } catch (Exception e) {
            log.error("cmd error", e);
        }
    }

    public void resize(String columns, String rows) {
        if (Objects.nonNull(columns) && Objects.nonNull(rows)) {
            this.columns = Integer.valueOf(columns);
            this.rows = Integer.valueOf(rows);
            if (Objects.nonNull(process)) {
                process.setWinSize(new WinSize(this.columns, this.rows));
            }
        }
    }

    public void onTerminalClose() {
        if (session.isValid()) {
            try {
                EntityDefault entityDefault = new EntityDefault();
                entityDefault.metaPut("type", "close");
                session.send("/", entityDefault);
            } catch (Exception e) {
                log.warn("session send fail", e);
            }
        }
        if (null != process && process.isAlive()) {
            process.destroy();
            IoUtil.close(process.getInputStream());
            IoUtil.close(process.getErrorStream());
            IoUtil.close(process.getOutputStream());
        }
        IoUtil.close(inputReader);
        IoUtil.close(errorReader);
        IoUtil.close(outputWriter);
        log.info("关闭终端, sessionId = {} , 线程池活跃数: {}, 线程池数量: {}",
                id, threadPool.getActiveCount(),
                threadPool.getPoolSize()
        );
    }
}
